import _ from 'lodash';
import React from 'react';
import { BackHandler, Linking } from 'react-native';
import {
  CommonActions,
  NavigationContainerRef,
  StackActions,
  DrawerActions,
} from '@react-navigation/native';
import { wait } from './promise';
import { TRootRouteParam, TRouteName, TRouteParam } from '@/types';

export namespace Nav {
  let _modalCount = 1;
  let _navRef: TNavRef | undefined;
  const _modalLsrs: TModalChangeListener[] = [];
  const _pageChangeLsrs: TPageChangeListener[] = [];

  export function init(ref?: TNavRef) {
    _navRef = ref;
    const sub = BackHandler.addEventListener('hardwareBackPress', onBackPress);
    return () => sub.remove();
  }

  function onBackPress() {
    if (canGoBack()) {
      goBack();
      return true;
    }
    if (canCloseModal()) {
      modalClose();
      return true;
    }
    return false;
  }

  export function onModalChange(lsr: TModalChangeListener) {
    _modalLsrs.push(lsr);
    return {
      off: () => {
        _.pull(_modalLsrs, lsr);
      },
    };
  }

  export async function modal<T extends TRouteName>(
    page: T,
    params?: TRouteParam<T>,
    delay: number = 2000
  ) {
    let floating = false;
    const current = _navRef?.current?.getCurrentRoute?.();
    for (const lsr of _pageChangeLsrs) {
      const res = await lsr(
        'modal',
        { page: current?.name as T, params: current?.params },
        { page, params }
      );
      floating = floating || !!res.floating;
    }
    _navRef = _(_modalLsrs)
      .map(lsr => lsr('open', floating))
      .compact()
      .first();
    if (!_navRef || !_navRef.current) {
      throw new Error(`Nav.modal: cannot modal ${page} with ${params}`);
    }
    const route = _navRef?.current?.getCurrentRoute?.();
    if (route?.name !== page) {
      _navRef?.current?.reset?.({
        index: 0,
        stale: true,
        routes: [{ name: page, params }],
      });
    }
    _modalCount++;
    await wait(delay);
  }

  export async function modalClose(disableAnimated?: boolean) {
    const current = _navRef?.current?.getCurrentRoute?.();
    let floating = false;
    for (const lsr of _pageChangeLsrs) {
      const res = await lsr('modalClose', {
        page: current?.name as any,
        params: current?.params,
      });
      floating = floating || !!res.floating;
    }
    const top = _navRef;
    const cmdStr = !!disableAnimated ? 'closeWithoutAnimated' : 'close';
    _navRef = _(_modalLsrs)
      .map(lsr => lsr(cmdStr, floating))
      .compact()
      .first();
    if (!_navRef || !_navRef.current) {
      throw new Error(`Nav.modalClose: modalClose failed`);
    }
    _modalCount--;
    if (!floating && top?.current && _modalCount > 1) {
      setTimeout(() => {
        top.current?.reset?.({
          index: 0,
          stale: true,
          routes: [{ name: 'Blank' }],
        });
      }, 250);
    }
  }

  export function canCloseModal() {
    return _modalCount > 1;
  }

  export async function navigate<T extends TRouteName>(
    page: T,
    params?: TRouteParam<T>
  ) {
    const current = _navRef?.current?.getCurrentRoute?.();
    for (const lsr of _pageChangeLsrs) {
      await lsr(
        'navigate',
        { page: current?.name as T, params: current?.params },
        { page, params }
      );
    }
    _navRef?.current?.navigate?.(page as any, params ?? {});
  }
  export async function openDrawer() {
    // Storage.set(STARTWITHNOTIFICATION, false);
    _navRef?.current?.dispatch(DrawerActions.openDrawer());
  }
  export async function closeDrawer() {
    _navRef?.current?.dispatch(DrawerActions.closeDrawer());
  }

  export async function jumpTo(
    routeName: 'HomeScreen' | 'Square',
    params?: object | undefined
  ) {
    _navRef?.current?.dispatch(DrawerActions.jumpTo(routeName, params));
  }

  export async function push<T extends TRouteName>(
    page: T,
    params?: TRouteParam<T>
  ) {
    const current = _navRef?.current?.getCurrentRoute?.();
    for (const lsr of _pageChangeLsrs) {
      await lsr(
        'push',
        { page: current?.name as T, params: current?.params },
        { page, params }
      );
    }
    _navRef?.current?.dispatch?.(StackActions.push(page, params ?? {}));
  }

  export async function goBack<T extends TRouteName>() {
    const current = _navRef?.current?.getCurrentRoute?.();
    const toPage = _.nth(_navRef?.current?.getState().routes, -2)?.name;
    for (const lsr of _pageChangeLsrs) {
      await lsr(
        'goBack',
        { page: current?.name as T, params: current?.params },
        { page: toPage as T, params: {} }
      );
    }
    _navRef?.current?.goBack?.();
  }

  export async function resetTop<T extends TRouteName>(
    page: T,
    params?: TRouteParam<T>
  ) {
    const current = _navRef?.current?.getCurrentRoute?.();
    const from = { page: current?.name as T, params: current?.params };
    for (const lsr of _pageChangeLsrs) {
      await lsr('resetTop', from, { page, params });
    }
    _navRef?.current?.reset?.({
      index: 0,
      stale: true,
      routes: [{ name: page, params }],
    });
  }

  export async function reset<T extends TRouteName>(
    page: T,
    params?: TRouteParam<T>
  ) {
    const current = _navRef?.current?.getCurrentRoute?.();
    const from = { page: current?.name as T, params: current?.params };
    for (const lsr of _pageChangeLsrs) {
      await lsr('reset', from, { page, params });
    }
    _navRef = _(_modalLsrs)
      .map(lsr => lsr('reset', false))
      .compact()
      .first();
    _navRef?.current?.reset?.({
      index: 0,
      stale: true,
      routes: [{ name: page, params }],
    });
  }

  /**
   * 销毁前 n 个route  并跳转到新页面
   * @param page 新页面, 为空则只销毁该页面
   * @param n 要销毁的层级 默认销毁上一个页面
   * @param params 跳转新页面的参数
   */
  export function destroyNPages<T extends TRouteName>(
    page?: T | null,
    n: number = 1,
    params?: TRouteParam<T>
  ) {
    const newRoute = page && { name: page, params: params };
    _navRef?.current?.dispatch?.(state => {
      const retainRoutes = state.routes.slice(0, state.routes.length - n);
      const routes = newRoute ? [...retainRoutes, newRoute] : retainRoutes;
      const newState = {
        ...state,
        routes: routes,
        index: routes.length - 1,
      };
      return CommonActions.reset(newState);
    });
  }

  export function canGoBack() {
    return _navRef?.current?.canGoBack?.();
  }
  const DEEP_LINK_DOMAIN = 'https://uek4.adj.st';
  const SCHEME_PREFIX = 'stanly://';
  const openUrlExclude = [DEEP_LINK_DOMAIN, SCHEME_PREFIX];
  function matchLinkPrefix(url: string) {
    const matchPrefix = openUrlExclude.filter(item => url.startsWith(item));
    return matchPrefix ? matchPrefix[0] : '';
  }
  export function registerURLSchema() {
    const sub = Linking.addEventListener('url', evt => {
      if (evt.url) openURL(evt.url);
    });
    Linking.getInitialURL().then(async url => {
      if (url) {
        while (!_navRef?.current) {
          await wait(1000);
        }
        openURL(url);
      }
    });
    return () => sub.remove();
  }

  export async function openURL(
    url: string,
    paramEx: Record<string, any> = {}
  ) {
    if (typeof url !== 'string') {
      return;
    }
    // if (
    //   url.slice(0, 4).toLocaleLowerCase() === 'http' &&
    //   url.indexOf(DEEP_LINK_DOMAIN) !== 0
    // ) {
    //   //TODO trivia需要改成通用浏览器
    //   if (url.includes('trivia')) {
    //     return Nav.navigate('GetInvitationCode', { ...paramEx, url });
    //   }
    //   return push('Browser', { ...paramEx, url });
    // }
    // if (!matchLinkPrefix(url)) {
    //   return;
    // }

    // url = url.replace(matchLinkPrefix(url), '');
    // if (url[0] === '/') url = url.slice(1);
    // const paramStr = url.split('?');
    // const cmds = paramStr[0].split('/');
    // const params: Record<string, any> = { ...paramEx };
    // if (paramStr?.length > 1) {
    //   const param = paramStr[1]!;
    //   const splited = param?.split('&')!;
    //   splited.forEach(aParam => {
    //     const splitedParam = aParam.split('=');
    //     if (splitedParam.length == 2) {
    //       let value: string | boolean = splitedParam[1]!;
    //       value = decodeURIComponent(value);
    //       if (value === 'true') value = true;
    //       if (value === 'false') value = false;
    //       params[splitedParam[0]!] = value;
    //       if (splitedParam[0] === 'sc') {
    //         Log.yk('sc', cmds[2]);
    //         Analytics.GALogEvent(
    //           { key: cmds[2], module: 'Share', sc: value as string },
    //           'show'
    //         );
    //       }
    //     }
    //   });
    // }
    // const methods = { navigate, push, modal, reset };
    // if (!methods[cmds[1]]) {
    //   return;
    // }
    // const logined = cmds[0] === 'logined' && Storage.getLoginInfo();
    // const unlogined = cmds[0] === 'unlogined' && !Storage.getLoginInfo();
    // if (cmds[0] === 'all' || logined || unlogined) {
    //   if (cmds[2] === 'Event') {
    //     // await Native.enterEvent(params['eventId'], true);
    //   } else if (cmds[2] === 'DrawerRoute') {
    //     const loginfo = Storage.getLoginInfo();
    //     if (!loginfo?.isArtist) {
    //       // await methods[cmds[1]](cmds[2] as never, params);
    //     }
    //   } else {
    //     await methods[cmds[1]](cmds[2] as never, params);
    //   }
    // }
  }

  export function onPageChange(lsr: TPageChangeListener) {
    _pageChangeLsrs.push(lsr);
    return {
      remove: () => {
        _.pull(_pageChangeLsrs, lsr);
      },
    };
  }

  export function getCurrentPageName() {
    const name = _navRef?.current?.getCurrentRoute?.()?.name || '';
    return name;
  }
  export function getCurrentPageParams<T extends TRouteName>(page: T) {
    const params = _navRef?.current?.getCurrentRoute?.()
      ?.params as TRouteParam<T>;
    return params;
  }
  export function getNavStackRoutes() {
    return _navRef?.current?.getRootState().routes;
  }
}

type TPageChangeListener<T extends TRouteName = TRouteName> = (
  cmd: string,
  fromRoute?: { page: T; params: TRouteParam<T> },
  toRoute?: { page: T; params: TRouteParam<T> }
) => Promise<{ floating?: boolean }>;

export type TNavContainer = NavigationContainerRef<TRootRouteParam>;
export type TNavRef = React.RefObject<NavigationContainerRef<TRootRouteParam>>;
export type TDawerNavRef = React.RefObject<
  NavigationContainerRef<TRootRouteParam>
>;
export type TModalChangeListener = (
  cmd: 'open' | 'close' | 'reset' | 'closeWithoutAnimated',
  background: boolean
) => TNavRef | undefined;
